#pragma once

#include <utils/Concepts.h>
#include <utils/config.h>

#include <boost/describe.hpp>
#include <boost/mp11.hpp>

#include <cstddef>
#include <stdexcept>

#include <QVariant>

namespace solar::internal::qt_extension
{
    template <std::size_t N, typename T>
        requires(N <= 0)
    void setParaImpl(std::size_t /*index*/, const QVariant& /*value*/, T* /*object*/)
    {
        throw std::invalid_argument("Wrong index");
    }

    template <std::size_t N, typename T>
    void setParaImpl(std::size_t index, const QVariant& value, T* object)
    {
        if (index == N - 1)
        {
            using D1 = boost::describe::describe_members<std::remove_reference_t<T>,
                                                         boost::describe::mod_private>;
            using V1 = boost::mp11::mp_at_c<D1, N - 1>;
            *object.*V1::pointer =
                value.value<std::remove_reference_t<decltype(*object.*V1::pointer)>>();
            return;
        }
        setParaImpl<N - 1>(index, value, object);
    }

    template <std::size_t N, typename T>
    void setPara(std::size_t index, const QVariant& value, T* object)
    {
        setParaImpl<N>(index, value, object);
    }

    template <std::size_t N, typename T>
        requires(N <= 0)
    void getParaImpl(std::size_t /*index*/, const T* const /*object*/, QVariant& /*result*/)
    {
        throw std::invalid_argument("Wrong index");
    }

    template <std::size_t N, typename T>
    void getParaImpl(std::size_t index, const T* const object, QVariant& result)
    {
        if (index == N - 1)
        {
            using D1 = boost::describe::describe_members<std::remove_reference_t<T>,
                                                         boost::describe::mod_private>;
            using V1 = boost::mp11::mp_at_c<D1, N - 1>;
            result.setValue(*object.*V1::pointer);
            return;
        }
        getParaImpl<N - 1>(index, object, result);
    }

    template <std::size_t N, typename T>
    auto getPara(std::size_t index, const T* const object) -> QVariant
    {
        QVariant result;
        getParaImpl<N>(index, object, result);
        return result;
    }
} // namespace solar::internal::qt_extension
